home *** CD-ROM | disk | FTP | other *** search
/ Aminet 15 / Aminet 15 - Nov 1996.iso / Aminet / disk / misc / AGMSSetSCSI.lha / AGMSSetSCSI.c < prev    next >
C/C++ Source or Header  |  1996-09-26  |  28KB  |  899 lines

  1. /******************************************************************************
  2.  * AGMSSetSCSI - A utility for setting SCSI mode page device parameters.
  3.  *
  4.  * $Header: Big:Programming/C/AGMSSetSCSI/RCS/AGMSSetSCSI.c,v 1.5 1996/09/26 20:27:32 AGMS Exp $
  5.  *
  6.  * Implemented by Alexander G. M. Smith, Ottawa Canada, agmsmith@achilles.net,
  7.  * agmsmith@bix.com, 71330.3173@compuserve.com, and various other places
  8.  * including the Ottawa Freenet.
  9.  *
  10.  * This code is put into the public domain by AGMS, so you can copy it,
  11.  * hack it up, sell it, and do whatever you want to it.
  12.  *
  13.  * Compile with the GNU C compiler, gcc version 2.7.0, use the command line:
  14.  *    gcc -v -noixemul -O2 -Wall AGMSSetSCSI.c
  15.  *
  16.  * Or compile with the SAS C compiler, version 6.56.  I use 16 bit integers,
  17.  * but that shouldn't matter (the GNU version uses 32).
  18.  *
  19.  * This program changes various parameters that SCSI devices have.  You should
  20.  * first get the device's manual to find out what the actual bits do.  It is
  21.  * very easy to make a mistake and wipe out your hard drive, so don't try this
  22.  * unless you know what you are doing.
  23.  *
  24.  * I wrote this in an attempt to turn off plug-and-play SCSI in a Quantum
  25.  * Fireball hard drive (thinking it was causing problems with the SCSI
  26.  * controller on my Apollo 2030 68030/25 CPU board), as well as to set hard
  27.  * drive spin-down timeout values and other goodies (fortunately Quantum has
  28.  * a web site (http://www.quantum.com/products/manuals/) that describe all
  29.  * the mode page parameters).
  30.  *
  31.  * $Log: AGMSSetSCSI.c,v $
  32.  * Revision 1.5  1996/09/26  20:27:32  AGMS
  33.  * Final bit of polishing done.
  34.  *
  35.  * Revision 1.4  1996/09/24  23:32:28  AGMS
  36.  * First working version.
  37.  *
  38.  * Revision 1.3  1996/09/23  15:40:43  AGMS
  39.  * Now does a mode sense command and parses the returned data.
  40.  *
  41.  * Revision 1.2  1996/09/15  15:35:55  AGMS
  42.  * Command line parsing finished.  Now works with SAS C.
  43.  *
  44.  * Revision 1.1  1996/09/08  17:24:08  AGMS
  45.  * Initial revision
  46.  */
  47.  
  48. #ifdef __SASC
  49.   #define __USE_SYSBASE 1
  50.     /* Need this to make the exec.library headers use the library base global
  51.     variable SysBase in SAS C (otherwise it just uses location $4). */
  52. #endif
  53.  
  54. #include <ctype.h>
  55. #include <stdlib.h>
  56. #include <stdio.h>
  57. #include <string.h>
  58.  
  59. #include <proto/dos.h>
  60. #include <proto/exec.h>
  61. #include <proto/utility.h>
  62.  
  63. #ifndef EXEC_EXECBASE_H
  64.   #include <exec/execbase.h>
  65. #endif
  66.  
  67. #ifndef EXEC_MEMORY_H
  68.   #include <exec/memory.h>
  69. #endif
  70.  
  71. #ifndef WORKBENCH_STARTUP_H
  72.   #include <workbench/startup.h>
  73. #endif
  74.  
  75. #ifndef DEVICES_SCSIDISK_H
  76.   #include <devices/scsidisk.h>
  77. #endif
  78.  
  79.  
  80.  
  81. #ifdef _GST
  82.   extern struct ExecBase *SysBase;
  83.     /* If using precompiled headers (GST - global symbol table) in SAS C, the
  84.     extern declaration in the headers doesn't take effect. */
  85.  
  86. #endif /* _GST */
  87.  
  88.  
  89.  
  90. #ifdef __GNUC__
  91. /******************************************************************************
  92.  * Stuff imported from the Libnix runtime library for use with GNU C.
  93.  */
  94.  
  95. extern struct WBStartup *_WBenchMsg;
  96.   /* The workbench startup message is put here by the runtime if started from
  97.   the workbench, NULL when started from the command line. */
  98.  
  99. const char __nocommandline;
  100.   /* Create this variable to make libnix turn off command line parsing. */
  101.  
  102. #endif /* __GNUC__ */
  103.  
  104.  
  105. #ifndef __AMIGADATE__
  106.   /* This doesn't look as good as the real __AMIGADATE__ format, but it's
  107.   close enough for me, if not the system's VERSION command. */
  108.   #define __AMIGADATE__ "(" __DATE__ ")"
  109. #endif
  110.  
  111.  
  112.  
  113. /******************************************************************************
  114.  * Command line argument parsing stuff.
  115.  */
  116.  
  117. static const char RDArgsTemplate [] =
  118. "DEVICE/K,UNIT/A/N,PAGE/A,CURRENT/S,MODIFIABLE/S,DEFAULT/S,SAVED/S,"
  119. "WRITECURRENT/S,WRITESAVED/S,CHANGES/M";
  120.   /* Template for parsing the command line, native Amiga style. */
  121.  
  122. enum RDArgsEnum {
  123.   DEVICE_RDARGINDEX = 0,
  124.   UNIT_RDARGINDEX,
  125.   PAGE_RDARGINDEX,
  126.   CURRENT_RDARGINDEX,
  127.   MODIFIABLE_RDARGINDEX,
  128.   DEFAULT_RDARGINDEX,
  129.   SAVED_RDARGINDEX,
  130.   WRITECURRENT_RDARGINDEX,
  131.   WRITESAVED_RDARGINDEX,
  132.   CHANGES_RDARGINDEX,
  133.   NUMRDARGS
  134. };
  135.   /* Argument numbers corresponding to the command line parsing template. */
  136.  
  137. #define MAXDEVICENAME 80
  138. char DeviceName [MAXDEVICENAME];
  139.   /* Name of the device driver to use, usually scsi.device. */
  140.  
  141. LONG UnitNumber;
  142.   /* SCSI disk unit to use, xyz decimal format, x is board number for multiple
  143.   SCSI controller boards (usually 0), y is LUN of subdevice (usually 0), z is
  144.   SCSI ID of device. */
  145.  
  146. int LUN;
  147.   /* Logical unit number derived from UnitNumber, 0 to 9. */
  148.  
  149. UBYTE PageNumber;
  150.   /* Which SCSI parameters mode page to look at and maybe change. */
  151.  
  152. enum DataSourceEnum {
  153.   CURRENT = 0,
  154.   MODIFIABLE,
  155.   DEFAULT,
  156.   SAVED,
  157.   MAXDATASOURCES
  158. } DataSource;
  159.   /* Which kind of data to read and write from the device. */
  160.  
  161. char *DataSourceNames [MAXDATASOURCES] =
  162. {
  163.   "current",
  164.   "modifiable",
  165.   "default",
  166.   "saved"
  167. };
  168.  
  169. BOOL WriteCurrent;
  170.   /* TRUE if the user wants to actually write the changes to the current SCSI
  171.   settings page. */
  172.  
  173. BOOL WriteSaved;
  174.   /* TRUE if the user wants to actually write the changes to the saved /
  175.   permanent SCSI settings page.  Actually, all current pages get copied to the
  176.   saved pages. */
  177.  
  178. struct ChangeStruct {
  179.   UBYTE offset;
  180.   UBYTE value;
  181. } ChangeArray [256];
  182. int NumberOfChanges;
  183.   /* The user's list of changes expressed in offsets and values. */
  184.  
  185. const char HelpText [] =
  186.   "AGMSSetSCSI is a utility for setting SCSI mode page device parameters.\n"
  187.   "Since this can destroy your SCSI device, you should only use it when\n"
  188.   "you know what you are doing!!!\n"
  189.   "\n"
  190.   "Public domain by Alexander G. M. Smith, Ottawa Canada, send questions to\n"
  191.   "agmsmith@achilles.net, agmsmith@bix.com, 71330.3173@compuserve.com.\n"
  192.   "$VER: AGMSSetSCSI 1.0 " __AMIGADATE__ " ($Id: AGMSSetSCSI.c,v 1.5 1996/09/26 20:27:32 AGMS Exp $)\n"
  193.   "\n"
  194.   "DEVICE is the name of the SCSI device driver, it defaults to a device\n"
  195.   "with SCSI in its name, or scsi.device if it can't find one.  You need\n"
  196.   "to type the keyword DEVICE if you want to specify it.\n"
  197.   "\n"
  198.   "UNIT is the decimal SCSI unit number, use tens for LUN and hundreds for\n"
  199.   "controller board number.  So 456 would be board 4 (5th board), LUN\n"
  200.   "(logical unit number, sub-unit of the device) number 5, at SCSI ID\n"
  201.   "number 6.  You need to specify a value for this one.\n"
  202.   "\n"
  203.   "PAGE is the mode page number, in hexadecimal.  Each page covers some\n"
  204.   "function, such as control of caching, control of idle time power down,\n"
  205.   "etc.  You need to specify this one.  See your drive manual for details\n"
  206.   "and other sources for the standard pages (like the ProbeSCSI program by\n"
  207.   "Ron Klinkien or http://www.quantum.com/products/manuals/).\n"
  208.   "\n"
  209.   "CURRENT, SAVED, DEFAULT, MODIFIABLE specify the source of the data.\n"
  210.   "Current is for the current settings (they are copied from the SAVED\n"
  211.   "values when your SCSI device starts up), SAVED is for the permanent\n"
  212.   "settings saved in the SCSI device, and DEFAULT is for the factory\n"
  213.   "settings.  MODIFIABLE is a special data source that has 1 bits in\n"
  214.   "positions which are modifiable.  Defaults to CURRENT.\n"
  215.   "\n"
  216.   "WRITECURRENT will make the program write your changes back to the SCSI\n"
  217.   "device's current settings, into the same page where they came from.\n"
  218.   "WRITESAVED writes all current pages to the permanent saved settings,\n"
  219.   "not just the one you were editing.  Defaults to not writing back the\n"
  220.   "values.  Only specify one of these two.\n"
  221.   "\n"
  222.   "CHANGES are a list of offset and value pairs.  Both are in hex and are\n"
  223.   "separated by a slash.  For example \"4/1a c/ff\" would set the byte at\n"
  224.   "offset $4 (4 decimal) in the page to $1A (26) and the byte at\n"
  225.   "offset $C (12) to $FF (255).\n"
  226.   "\n";
  227.  
  228.  
  229.  
  230. /******************************************************************************
  231.  * Things too large to be on the stack or that are global.
  232.  */
  233.  
  234. char ErrorMessage [256];
  235.   /* For formatting error messages into. */
  236.  
  237. UBYTE ModePageData [256];
  238.   /* The currently read in mode page. */
  239.  
  240. int ModePageDataLength;
  241.   /* Number of bytes of data in the ModePageData array. */
  242.  
  243. UBYTE MediumType;
  244.   /* Saved data from the Mode Sense that needs to be regurgitated
  245.   for the Mode Select command. */
  246.  
  247. UBYTE ChangedModePageData [256];
  248.   /* The new mode page data, after the user's changes. */
  249.  
  250.  
  251.  
  252. /******************************************************************************
  253.  * Data structures needed for talking to scsi.device.
  254.  */
  255.  
  256. struct MsgPort *MyMessagePort;
  257.   /* For receiving replies to device requests. */
  258.  
  259. struct IOStdReq *MyIORequest;
  260.   /* Commands to scsi.device are put into this IO request record. */
  261.  
  262. struct SCSICmd *MySCSICommand;
  263.   /* The IO request points to this record which points to the various buffers
  264.   needed for running the command.  If the buffer pointer fields in this record
  265.   are NULL then the buffers haven't been allocated yet. */
  266.  
  267. #define SCSI_BUFFER_SIZES 256
  268.   /* All the buffers (data, command, sense) in MySCSICommand are this size and
  269.   are allocated from DMA-able memory.  Though scsi.device is reported to use
  270.   programmed IO rather than DMA for transfers <256 bytes. */
  271.  
  272. BOOL DeviceWasOpened;
  273.   /* TRUE if the scsi.device was OpenDevice'd and needs to be closed when the
  274.   program exits. */
  275.  
  276. struct SCSIModeSenseStruct
  277. {
  278.   UBYTE commandCode;
  279.     /* SCSI command opcode: high 3 bits are command group, low 5 are command,
  280.     group 0 is 6 bytes, group 1 & 2 are 10 bytes).  Mode sense command is $1a,
  281.     Mode select is $15.  The rest of this structure is set up for group 0
  282.     mode sense commands since most devices should support them.  The mode
  283.     sense command reads one or more pages of SCSI parameter data. */
  284.   UBYTE lun;
  285.     /* Logical unit number, for subdevices off a main one, in high 3 bits.
  286.     Usually 0, sometimes used for jukebox devices.  Bit $08 can be set to turn
  287.     off sending of block descriptors in the sense data. */
  288.   UBYTE pageCode;
  289.     /* Which page of data to read for MODE SENSE.  The high 2 bits specify the
  290.     data source, 00=current data, 01=fake page with all modifiable bits set to
  291.     one, 10=default values, 11=saved values.  Low 6 bits are page number. */
  292.   UBYTE reserved1;
  293.     /* Set this to zero for MODE SENSE. */
  294.   UBYTE allocationLength;
  295.     /* The number of bytes of returned sense data we can accept.  We will use
  296.     $FE since our returned sense data buffer is at least that big and the
  297.     number has to be even for scsi.device. */
  298.   UBYTE reserved2;
  299.     /* Also set this to zero.  Bits used for linked commands. */
  300. };
  301.  
  302. struct SCSIModeSelectStruct
  303. {
  304.   UBYTE commandCode;
  305.     /* SCSI command opcode.  Mode select command is $15.  This command writes
  306.     to a page of SCSI parameter settings. */
  307.   UBYTE lun;
  308.     /* Logical unit number, for subdevices off a main one, in high 3 bits.
  309.     There are some other control bits, but leave them zero.  Least significant
  310.     bit (value $01) saves the parameters permanently (all the current settings
  311.     pages are saved, not just the page being changed). */
  312.   UBYTE reserved1;
  313.     /* Set this to zero for MODE SELECT. */
  314.   UBYTE reserved2;
  315.     /* Set this to zero for MODE SELECT. */
  316.   UBYTE parameterLength;
  317.     /* The number of bytes of outgoing sense data, including headers etc.
  318.     Actual amount depends on the page being set.  4 byte header (we don't
  319.     send the block descriptor), plus a 2 byte page header (we just send
  320.     one page), plus page data (same size as was in mode sense page data). */
  321.   UBYTE reserved3;
  322.     /* Also set this to zero.  Bits used for linked commands. */
  323. };
  324.  
  325.  
  326.  
  327. /******************************************************************************
  328.  * Look at the argument values the user specified and try to make sense of
  329.  * them.  Also generates defaults for unspecified settings.
  330.  */
  331.  
  332. static BOOL ExamineArguments (LONG ArgumentValues [NUMRDARGS])
  333. {
  334.   char         *ChangeStart;
  335.   char         *ChangeString;
  336.   struct Node  *CurrentNode;
  337.   BOOL          Success;
  338.   char         *FromChar;
  339.   int           i;
  340.   char         *ParseString;
  341.   char        **StringPntrPntr;
  342.   char         *ToChar;
  343.  
  344.   /* Get the device name.  Search device list for something with SCSI
  345.      in it if the user didn't specify a value. */
  346.  
  347.   if (ArgumentValues [DEVICE_RDARGINDEX] == 0)
  348.   {
  349.     CurrentNode = SysBase->DeviceList.lh_Head;
  350.     Success = FALSE;
  351.     while (CurrentNode->ln_Succ != NULL)
  352.     {
  353.       /* Copy the potential device name into our device name buffer and
  354.          convert it to upper case for comparison purposes.  Also truncates
  355.          names that are too long. */
  356.  
  357.       for (FromChar = CurrentNode->ln_Name, ToChar = DeviceName, i = 0;
  358.       *FromChar && i < MAXDEVICENAME - 1;
  359.       ++FromChar, ++ToChar, ++i)
  360.         *ToChar = toupper (*FromChar);
  361.       *ToChar = 0;
  362.  
  363.       if (strstr (DeviceName, "SCSI"))
  364.       {
  365.         strncpy (DeviceName, CurrentNode->ln_Name, MAXDEVICENAME);
  366.         Success = TRUE;
  367.         break;
  368.       }
  369.  
  370.       CurrentNode = CurrentNode->ln_Succ;
  371.     }
  372.  
  373.     if (!Success)
  374.       strcpy (DeviceName, "scsi.device");
  375.   }
  376.   else
  377.     strncpy (DeviceName, (char *) (ArgumentValues [DEVICE_RDARGINDEX]),
  378.     MAXDEVICENAME);
  379.  
  380.   DeviceName [MAXDEVICENAME-1] = 0; /* In case strncpy failed. */
  381.  
  382.   /* Read the unit number.  The reference is a pointer to the long number. */
  383.  
  384.   if (ArgumentValues [UNIT_RDARGINDEX] != 0)
  385.   {
  386.     UnitNumber = *((LONG *) (ArgumentValues [UNIT_RDARGINDEX]));
  387.     LUN = (UnitNumber / 10) % 10;
  388.   }
  389.  
  390.   /* And now the page number. */
  391.  
  392.   if (ArgumentValues [PAGE_RDARGINDEX] != 0)
  393.   {
  394.     PageNumber = strtol ((char *) (ArgumentValues [PAGE_RDARGINDEX]),
  395.     NULL /* No output endpointer */, 16 /* Base for conversion */);
  396.     PageNumber &= 0x3F; /* Values are from 0 to $3F. */
  397.   }
  398.  
  399.   /* Find the data source. */
  400.  
  401.   for (i = CURRENT_RDARGINDEX; i <= SAVED_RDARGINDEX; ++i)
  402.   {
  403.     if (ArgumentValues [i])
  404.       DataSource = i - CURRENT_RDARGINDEX;
  405.   }
  406.  
  407.   /* Does the user wants to actually write the changes? */
  408.  
  409.   if (ArgumentValues [WRITECURRENT_RDARGINDEX])
  410.     WriteCurrent = TRUE;
  411.  
  412.   if (ArgumentValues [WRITESAVED_RDARGINDEX])
  413.     WriteSaved = TRUE;
  414.  
  415.   /* Collect the user's list of changes and convert to binary. */
  416.  
  417.   if (ArgumentValues [CHANGES_RDARGINDEX] != 0)
  418.   {
  419.     StringPntrPntr = (char **) (ArgumentValues [CHANGES_RDARGINDEX]);
  420.     while ((ChangeString = *StringPntrPntr++) != NULL && NumberOfChanges < 256)
  421.     {
  422.       Success = FALSE;
  423.  
  424.       ChangeArray [NumberOfChanges] . offset =
  425.       strtol (ChangeString, &ParseString, 16);
  426.  
  427.       if (ParseString != NULL && *ParseString /* there's more */ &&
  428.       ParseString != ChangeString /* some characters were used */)
  429.       {
  430.         ++ParseString; /* Skip over the offset/value separator. */
  431.         ChangeStart = ParseString;
  432.  
  433.         ChangeArray [NumberOfChanges] . value =
  434.         strtol (ParseString, &ParseString, 16);
  435.  
  436.         if (ParseString != ChangeStart /* some was used */)
  437.         {
  438.           ++NumberOfChanges;
  439.           Success = TRUE;
  440.         }
  441.       }
  442.       if (!Success)
  443.         printf ("Change \"%s\" ignored (want hex number, \"/\", hex #).\n",
  444.         ChangeString);
  445.     }
  446.   }
  447.  
  448.   printf ("Using %s unit %ld (ID %d, LUN %d), page $%02x, \"%s\" data source.\n",
  449.   DeviceName, UnitNumber, (int) (UnitNumber % 10), (int) LUN, (int) PageNumber,
  450.   DataSourceNames [DataSource]);
  451.  
  452.   return TRUE;
  453. }
  454.  
  455.  
  456.  
  457. /******************************************************************************
  458.  * Read the command line arguments and set up various things.  Returns TRUE
  459.  * if successful.
  460.  */
  461.  
  462. static BOOL ParseArguments (void)
  463. {
  464.   LONG            ArgumentValues [NUMRDARGS];
  465.   struct TagItem  DoneTag = {TAG_DONE, 0};
  466.   LONG            ErrorCode;
  467.   struct RDArgs  *RDArgParametersPntr;
  468.   BOOL            ReturnCode;
  469.  
  470.   if (_WBenchMsg != NULL)
  471.     return FALSE;  /* If started from the workbench, no command line exists. */
  472.  
  473.   RDArgParametersPntr = AllocDosObject (DOS_RDARGS, &DoneTag);
  474.   if (RDArgParametersPntr == NULL)
  475.   {
  476.     printf ("Out of memory for allocating RDARGS structure.\n");
  477.     return FALSE;
  478.   }
  479.  
  480.   RDArgParametersPntr->RDA_ExtHelp = (char *) HelpText;
  481.  
  482.   memset (ArgumentValues, 0, sizeof (ArgumentValues));
  483.  
  484.   ReturnCode = (NULL != ReadArgs ((STRPTR) RDArgsTemplate,
  485.   ArgumentValues, RDArgParametersPntr));
  486.  
  487.   if (ReturnCode)
  488.     ReturnCode = ExamineArguments (ArgumentValues);
  489.   else
  490.   {
  491.     ErrorCode = IoErr ();
  492.     strcpy (ErrorMessage, ": ?");
  493.     Fault (ErrorCode, (char *) "", ErrorMessage, sizeof (ErrorMessage));
  494.     printf ("Oops%s, please use \"AGMSSetSCSI ?\" for\n"
  495.     "help and type a second \"?\" to get even more help.\n", ErrorMessage);
  496.   }
  497.  
  498.   FreeArgs (RDArgParametersPntr);
  499.   FreeDosObject (DOS_RDARGS, RDArgParametersPntr);
  500.  
  501.   return ReturnCode;
  502. }
  503.  
  504.  
  505.  
  506. /******************************************************************************
  507.  * Opens the scsi.device driver and sets up the related data structures.
  508.  * Returns TRUE if successful.
  509.  */
  510.  
  511. BOOL OpenSCSIDevice (void)
  512. {
  513.   int ErrorCode;
  514.  
  515.   MyMessagePort = CreateMsgPort ();
  516.   if (MyMessagePort == NULL)
  517.   {
  518.     printf ("Out of memory for CreateMsgPort.\n");
  519.     return FALSE;
  520.   }
  521.  
  522.   MyIORequest = CreateIORequest (MyMessagePort, sizeof (struct IOStdReq));
  523.   if (MyIORequest == NULL)
  524.   {
  525.     printf ("Out of memory for CreateIORequest.\n");
  526.     return FALSE;
  527.   }
  528.  
  529.   ErrorCode =  OpenDevice (DeviceName, UnitNumber,
  530.   (struct IORequest *) MyIORequest, 0L);
  531.   if (ErrorCode != 0)
  532.   {
  533.     printf ("OpenDevice for %s unit %d failed, code %d.\n",
  534.     DeviceName, (int) UnitNumber, ErrorCode);
  535.     return FALSE;
  536.   }
  537.  
  538.   MySCSICommand = AllocMem (sizeof (struct SCSICmd), MEMF_PUBLIC | MEMF_CLEAR);
  539.   if (MySCSICommand == NULL)
  540.   {
  541.     printf ("Out of memory for SCSICmd record.\n");
  542.     return FALSE;
  543.   }
  544.  
  545.   /* Set up for a direct SCSI command through the scsi.device. */
  546.  
  547.   MyIORequest->io_Command = HD_SCSICMD;
  548.   MyIORequest->io_Data = MySCSICommand;
  549.   MyIORequest->io_Length = sizeof (struct SCSICmd);
  550.  
  551.   MySCSICommand->scsi_Data =
  552.     AllocMem (SCSI_BUFFER_SIZES, MEMF_24BITDMA | MEMF_PUBLIC);
  553.   MySCSICommand->scsi_Command =
  554.     AllocMem (SCSI_BUFFER_SIZES, MEMF_24BITDMA | MEMF_PUBLIC);
  555.   MySCSICommand->scsi_SenseData =
  556.     AllocMem (SCSI_BUFFER_SIZES, MEMF_24BITDMA | MEMF_PUBLIC);
  557.  
  558.   if (MySCSICommand->scsi_Data == NULL ||
  559.   MySCSICommand->scsi_Command == NULL ||
  560.   MySCSICommand->scsi_SenseData == NULL)
  561.   {
  562.     printf ("Ran out of 24 bit DMA accessible memory for data buffers.\n");
  563.     return FALSE;
  564.   }
  565.  
  566.   return TRUE;
  567. }
  568.  
  569.  
  570.  
  571. /******************************************************************************
  572.  * Submits the SCSI command in MyIORequest/MySCSICommand to the scsi.device
  573.  * and prints out any resulting errors.  Returns TRUE if no errors.
  574.  */
  575.  
  576. BOOL SubmitSCSICommand (void)
  577. {
  578.   int ErrorCode;
  579.   int i;
  580.  
  581.   MySCSICommand->scsi_Status = 0;  /* In case a *.device IO error happens. */
  582.   MySCSICommand->scsi_SenseActual = 0; /* Some scsi drivers don't set this. */
  583.  
  584.   ErrorCode = DoIO ((struct IORequest *) MyIORequest);
  585.   if (ErrorCode != 0)
  586.     printf ("DoIO failed for the SCSI command, code %d.\n", ErrorCode);
  587.  
  588.   if (MySCSICommand->scsi_Status != 0)
  589.   {
  590.     printf ("SCSI command failed, SCSI error code %d.",
  591.     (int) MySCSICommand->scsi_Status);
  592.  
  593.     if (MySCSICommand->scsi_SenseActual > 0)
  594.     {
  595.       printf ("  Associated sense data:\n");
  596.       for (i = 0; i < MySCSICommand->scsi_SenseActual; i++)
  597.         printf ("$%02x ", (int) MySCSICommand->scsi_SenseData[i]);
  598.     }
  599.     printf ("\n");
  600.     return FALSE;
  601.   }
  602.   return (ErrorCode == 0);
  603. }
  604.  
  605.  
  606.  
  607. /******************************************************************************
  608.  * Load in the mode sense values from the page the user asked for and display
  609.  * them on stdout as well as storing them in ModePageData.  Returns TRUE if
  610.  * successful, FALSE if the device doesn't have that page or some other
  611.  * error happens.
  612.  */
  613.  
  614. BOOL ReadSCSISettings (void)
  615. {
  616.   BOOL                        ChangeableBit;
  617.   BOOL                        FoundOurPage;
  618.   int                         i;
  619.   int                         Index;
  620.   int                         MaxIndex;
  621.   struct SCSIModeSenseStruct *ModeSenseCommand;
  622.   UBYTE                      *ModeSenseData;
  623.   int                         PageID;
  624.   int                         PageLength;
  625.  
  626.   ModeSenseCommand = (void *) MySCSICommand->scsi_Command;
  627.   ModeSenseCommand->commandCode = 0x1A; /* Mode sense command. */
  628.   ModeSenseCommand->lun = (LUN << 5);
  629.   ModeSenseCommand->pageCode = (DataSource << 6) | PageNumber;
  630.   ModeSenseCommand->reserved1 = 0;
  631.   ModeSenseCommand->allocationLength = 254; /* Must be even. */
  632.   ModeSenseCommand->reserved2 = 0;
  633.  
  634.   MySCSICommand->scsi_Length = 254; /* Can take this much answer data. */
  635.   MySCSICommand->scsi_CmdLength = sizeof (struct SCSIModeSenseStruct);
  636.   MySCSICommand->scsi_SenseLength = 254; /* Error message buffer size. */
  637.   MySCSICommand->scsi_Flags = SCSIF_READ | SCSIF_AUTOSENSE;
  638.  
  639.   if (!SubmitSCSICommand ())
  640.     return FALSE;
  641.  
  642.   /* Analyse the data received from the mode sense. */
  643.  
  644.   ModeSenseData = (void *) MySCSICommand->scsi_Data;
  645.   if (MySCSICommand->scsi_Actual == 0)
  646.   {
  647.     printf ("No data received at all from the mode sense command!\n");
  648.     return FALSE;
  649.   }
  650.  
  651.   printf ("Received %ld mode sense bytes.  Raw mode sense data in hexadecimal:\n",
  652.   (long) MySCSICommand->scsi_Actual);
  653.   for (Index = 0; Index < (int) MySCSICommand->scsi_Actual; Index++)
  654.   {
  655.     printf (" %02x", (int) ModeSenseData [Index]);
  656.     if (Index % 16 == 15)
  657.       printf ("\n");
  658.   }
  659.   if (Index % 16 != 0)
  660.     printf ("\n");
  661.  
  662.   MaxIndex = ModeSenseData [0]; /* First byte is count of remainder. */
  663.   if (MaxIndex >= MySCSICommand->scsi_Actual)
  664.   {
  665.     printf ("Header says there is more mode sense data (%d) than we\n"
  666.     "received (%d).  Using only what we recevied.\n", MaxIndex,
  667.     (int) MySCSICommand->scsi_Actual);
  668.     MaxIndex = MySCSICommand->scsi_Actual;
  669.   }
  670.   else
  671.     MaxIndex++; /* Length byte not included in length. */
  672.  
  673.   /* MaxIndex is one past last valid data array index. */
  674.  
  675.   Index = 3;  /* Index of size of descriptor blocks, in standard header. */
  676.   if (Index >= MaxIndex)
  677.   {
  678.     printf ("Mode sense data too short for even standard header!\n");
  679.     return FALSE;
  680.   }
  681.  
  682.   MediumType = ModeSenseData [1];  /* Save this field for later use. */
  683.  
  684.   Index = ModeSenseData [Index] + 4;  /* Skip past block descriptors. */
  685.  
  686.   FoundOurPage = FALSE;
  687.   while (Index < MaxIndex)
  688.   {
  689.     PageID = ModeSenseData [Index];
  690.     PageID &= 0x3F; /* Only lower 6 bits are for the page number. */
  691.     ChangeableBit = (ModeSenseData [Index] & 0x80) != 0;
  692.     PageLength = ModeSenseData [Index+1];
  693.     printf ("Found page $%02x, %s, containing %d bytes:\n",
  694.     PageID, ChangeableBit ? "changeable" : "read only", PageLength);
  695.     Index += 2;
  696.  
  697.     if (Index + PageLength > MaxIndex)
  698.     {
  699.       printf ("Oops, that page would run past the end of the received data!\n");
  700.       return FALSE;
  701.     }
  702.  
  703.     printf ("\t");
  704.     for (i = 0; i < PageLength; i++)
  705.       printf (" %02x", (int) i);
  706.     printf ("\n\t");
  707.     for (i = 0; i < PageLength; i++)
  708.       printf (" %02x", (int) ModeSenseData [Index + i]);
  709.     printf ("\n");
  710.  
  711.     if (PageLength == 0)
  712.     {
  713.       printf ("Wait a sec, page length zero?  "
  714.       "Is this an old NEC CDROM?  Bye-bye!\n");
  715.       return FALSE;
  716.     }
  717.  
  718.     if (PageID == PageNumber)
  719.     {
  720.       /* Found the page the user is looking for, copy the data to
  721.          our working array. */
  722.  
  723.       FoundOurPage = TRUE;
  724.       ModePageDataLength = PageLength;
  725.  
  726.       for (i = 0; i < PageLength; i++)
  727.         ModePageData [i] = ModeSenseData [Index + i];
  728.     }
  729.  
  730.     Index += PageLength; /* Advance to the next page. */
  731.   }
  732.  
  733.   if (FoundOurPage)
  734.     return TRUE;
  735.  
  736.   printf ("The SCSI device didn't provide your requested mode sense page!\n");
  737.   return FALSE;
  738. }
  739.  
  740.  
  741.  
  742. /******************************************************************************
  743.  * Write back the changed settings to the given mode page and data
  744.  * destination.  Returns TRUE if successful (no errors, including not
  745.  * writing case).
  746.  */
  747.  
  748. BOOL WriteSCSISettings (void)
  749. {
  750.   enum DataSourceEnum           DataDestination;
  751.   int                           i;
  752.   struct SCSIModeSelectStruct  *ModeSelectCommand;
  753.   UBYTE                        *ModeSelectData;
  754.  
  755.   /* Apply the changes to the data. */
  756.  
  757.   for (i = 0; i < ModePageDataLength; i++)
  758.     ChangedModePageData [i] = ModePageData [i];
  759.   for (i = 0; i < NumberOfChanges; i++)
  760.     ChangedModePageData [ChangeArray [i] . offset] =
  761.     ChangeArray [i] . value;
  762.  
  763.   /* Print the changed numbers in highlighted mode. */
  764.  
  765.   printf ("Now have %d bytes of changed data for page $%02x:\n\t",
  766.   ModePageDataLength, (int) PageNumber);
  767.   for (i = 0; i < ModePageDataLength; i++)
  768.     printf ((ChangedModePageData [i] == ModePageData [i]) ?
  769.     " %02x" : " \033[2m%02x\033[m", (int) ChangedModePageData [i]);
  770.   printf ("\n");
  771.  
  772.   /* Decide where to write the data, if anywhere. */
  773.  
  774.   if (WriteSaved)
  775.     DataDestination = SAVED;
  776.   else if (WriteCurrent)
  777.     DataDestination = CURRENT;
  778.   else
  779.   {
  780.     printf ("Mode page data not written back, as you requested.\n");
  781.     return TRUE;
  782.   }
  783.  
  784.   printf ("Writing to %s data for page $%02x...\n",
  785.   DataSourceNames [DataDestination], (int) PageNumber);
  786.   if (DataDestination == SAVED)
  787.     printf ("Note that writing to saved data writes ALL the current pages,\n"
  788.     "not just the one you are editing.  So, if you changed other\n"
  789.     "current pages, those changes will be saved too.\n");
  790.  
  791.   ModeSelectCommand = (void *) MySCSICommand->scsi_Command;
  792.   ModeSelectCommand->commandCode = 0x15; /* Mode select command. */
  793.   ModeSelectCommand->lun = (LUN << 5);
  794.   if (DataDestination == SAVED)
  795.     ModeSelectCommand->lun |= 1; /* Set low bit to save permanently. */
  796.   ModeSelectCommand->reserved1 = 0;
  797.   ModeSelectCommand->reserved2 = 0;
  798.   ModeSelectCommand->parameterLength = 4 + 2 + ModePageDataLength;
  799.   ModeSelectCommand->reserved3 = 0;
  800.  
  801.   MySCSICommand->scsi_Length = ModeSelectCommand->parameterLength;
  802.   MySCSICommand->scsi_CmdLength = sizeof (struct SCSIModeSelectStruct);
  803.   MySCSICommand->scsi_SenseLength = 254; /* Error message buffer size. */
  804.   MySCSICommand->scsi_Flags = SCSIF_WRITE | SCSIF_AUTOSENSE;
  805.  
  806.   /* Parameter list data for the mode select command to use.  First comes the
  807.      4 byte parameter list header. */
  808.  
  809.   ModeSelectData = (void *) MySCSICommand->scsi_Data;
  810.  
  811.   ModeSelectData [0] = 0; /* Reserved zero. */
  812.   ModeSelectData [1] = MediumType; /* Regurgitating. */
  813.   ModeSelectData [2] = 0; /* Reserved zero. */
  814.   ModeSelectData [3] = 0; /* Block descriptor length, we don't have one. */
  815.  
  816.   /* No block descriptors, so we move right into a mode page header. */
  817.  
  818.   ModeSelectData [4] = PageNumber;
  819.   ModeSelectData [5] = ModePageDataLength;
  820.  
  821.   /* And finally the actual data. */
  822.  
  823.   for (i = 0; i < ModePageDataLength; i++)
  824.     ModeSelectData [6 + i] = ChangedModePageData [i];
  825.  
  826.   if (!SubmitSCSICommand ())
  827.     return FALSE;
  828.  
  829.   printf ("Write successfully completed.\n");
  830.   return TRUE;
  831. }
  832.  
  833.  
  834.  
  835. /******************************************************************************
  836.  * Called at program exit to clean up globally allocated things.
  837.  */
  838.  
  839. void CleanUp (void)
  840. {
  841.   if (DeviceWasOpened)
  842.   {
  843.     CloseDevice ((struct IORequest *) MyIORequest);
  844.     DeviceWasOpened = FALSE;
  845.   }
  846.  
  847.   if (MySCSICommand != NULL)
  848.   {
  849.     if (MySCSICommand->scsi_Data != NULL)
  850.       FreeMem (MySCSICommand->scsi_Data, SCSI_BUFFER_SIZES);
  851.  
  852.     if (MySCSICommand->scsi_Command != NULL)
  853.       FreeMem (MySCSICommand->scsi_Command, SCSI_BUFFER_SIZES);
  854.  
  855.     if (MySCSICommand->scsi_SenseData != NULL)
  856.       FreeMem (MySCSICommand->scsi_SenseData, SCSI_BUFFER_SIZES);
  857.  
  858.     FreeMem (MySCSICommand, sizeof (struct SCSICmd));
  859.     MySCSICommand = NULL;
  860.   }
  861.  
  862.   if (MyIORequest != NULL)
  863.   {
  864.     DeleteIORequest (MyIORequest);
  865.     MyIORequest = NULL;
  866.   }
  867.  
  868.   if (MyMessagePort != NULL)
  869.   {
  870.     DeleteMsgPort (MyMessagePort);
  871.     MyMessagePort = NULL;
  872.   }
  873. }
  874.  
  875.  
  876.  
  877. /******************************************************************************
  878.  * The usual main program entry point.
  879.  */
  880.  
  881. int main (void)
  882. {
  883.   atexit (CleanUp);
  884.  
  885.   if (!ParseArguments ())
  886.     return 10; /* Failure somewhere, or run from Workbench, not shell. */
  887.  
  888.   if (!OpenSCSIDevice ())
  889.     return 20; /* Failed to open and allocate things. */
  890.  
  891.   if (!ReadSCSISettings ())
  892.     return 21; /* Failed to access the SCSI device's settings. */
  893.  
  894.   if (!WriteSCSISettings ())
  895.     return 22; /* Something went wrong while setting the SCSI attributes. */
  896.  
  897.   return 0;  /* Success! */
  898. }
  899.